/*
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "functionplot.h"
#include "../math/function.h"
/// qt
#include <QWidget>
#include <QMouseEvent>
#include <QDebug>
#include <QTimer>

///qwt
#include <qwt_plot_curve.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_grid.h>
///std
#include <cmath>

#include <boost/weak_ptr.hpp>
UI::FunctionPlot::FunctionPlot(QWidget* pParent) : QwtPlot(QwtText( "function") ,pParent) {
    m_XMin = -5 ; 
    m_XMax = 5 ;
    m_YMin = -2 ; 
    m_YMax = 2 ; 
    m_Step = 0.01; 
    m_NumberOfValues = (int) ( abs(m_XMin) + abs(m_XMax) ) / m_Step ; 
    
    setAxisScale(0,m_YMin,m_YMax);
    setAxisScale(2,m_XMin,m_XMax);
    setMouseTracking( true ); 
    setFrameStyle(QFrame::Panel | QFrame::Raised);
    setLineWidth(2);
    canvas()->setPalette(QPalette( Qt::white ));
    QwtPlotGrid *pGrid = new QwtPlotGrid() ; 
    pGrid->attach(this);
    replot();
    m_pQObjectHack = new UI::QObjectHack(this);
}
void UI::FunctionPlot::addFunction( const QString & _equation ) {
    
    Math::IFunction<double> *pFunction = new Math::IFunction<double>( ) ; 
    pFunction->setEquation(_equation);
    pFunction->setVars( QStringList() << "x" );
    addFunction(pFunction);
}
void UI::FunctionPlot::addFunction( Math::IFunction< double >* _pFunction) {
    _pFunction->recalculateData( m_NumberOfValues , m_XMin , m_XMax , m_Step ) ;
    QString txt = _pFunction->equation().isEmpty() ? _pFunction->description() : _pFunction->equation();
    QwtPlotCurve *pCurve = new QwtPlotCurve( txt ) ; 
    pCurve->setRawData( _pFunction->range(), _pFunction->values() , _pFunction->size() );
    pCurve->attach( this );
    m_MapOfFunctions.insert(tMapKey(_pFunction), tMapValue(pCurve) );
    replot();
    m_pQObjectHack->functionAdded(txt) ; 
}

void UI::FunctionPlot::addFunctionDerivative( const Math::IFunction<double> * _pFunction) {
    Math::IFunction<double> * pDerivative = _pFunction->calculateDerivative( xMin() , xMax() , m_Step ) ; 
    if ( pDerivative == NULL ) {
        qDebug() << "Derivative is NULL " ; 
        return ; 
    }
    QString txt = QString("Derivative of ") + QString(_pFunction->equation().isEmpty() ? _pFunction->description() : _pFunction->equation() );
    pDerivative->setDescription(txt);
    
    QwtPlotCurve *pCurve = new QwtPlotCurve( txt ) ; 
    pCurve->setRawData( pDerivative->range(), pDerivative->values() , pDerivative->size() );
    pCurve->attach( this );
    pCurve->setPen(QPen(QColor::fromRgb(0,0,255)));
    m_MapOfFunctions.insert(tMapKey(pDerivative), tMapValue(pCurve) );
    replot();
    m_pQObjectHack->functionAdded( txt );
}
void UI::FunctionPlot::removeFunction(const Math::IFunction< double >* _pFunction) {
    ///FIXME: use find()
    UI::FunctionPlot::tMapType::iterator it = m_MapOfFunctions.begin() ;
    UI::FunctionPlot::tMapType::iterator itEnd = m_MapOfFunctions.end() ;
    QString txt =_pFunction->equation().isEmpty() ? _pFunction->description() : _pFunction->equation();
    for ( it ; it != itEnd ; ++it ) {
        if ( it.key().get() == _pFunction ) {
            it.value()->detach();
            it = m_MapOfFunctions.erase(it);
        }
    }
    replot() ; 
    m_pQObjectHack->functionRemoved( txt );
}
Math::IFunction<double>* UI::FunctionPlot::findFunction(const QString& funEq) {
    tMapType::const_iterator it = m_MapOfFunctions.begin() ;
    tMapType::const_iterator itEnd = m_MapOfFunctions.end() ; 
    while ( it != itEnd ) {
        if ( it.key()->equation().compare(funEq) == 0 || it.key()->description().compare(funEq) == 0 )
            return it.key().get() ; 
        ++it ; 
    }
    return NULL ;
}
void UI::FunctionPlot::replot() {
    tMapType::const_iterator it = m_MapOfFunctions.begin() ;
    tMapType::const_iterator itEnd = m_MapOfFunctions.end() ; 
    while ( it != itEnd ) {
        if ( it.key()->needToRecalculate() ) {
            it.key()->recalculateData(m_NumberOfValues, xMin(), xMax(),m_Step ) ; 
        }
        ++it ; 
    }
    QwtPlot::replot();
}
void UI::FunctionPlot::setXMin( double _value ) { 
    m_XMin = _value ;
    m_NumberOfValues = (int) ( abs(m_XMin) + abs(m_XMax) ) / m_Step ; 
} 
void UI::FunctionPlot::setXMax( double _value ) { 
    m_XMax = _value ; 
    m_NumberOfValues = ( int ) ( abs(m_XMin) + abs(m_XMax) ) / m_Step ; 
} 
void UI::FunctionPlot::setYMin( double _value ) { 
    m_YMin = _value ; 
    setAxisScale(0,m_YMin,m_YMax);
    QwtPlot::replot();
} 
void UI::FunctionPlot::setYMax( double _value ) { 
    m_YMax = _value ; 
    setAxisScale(0,m_YMin,m_YMax);
    QwtPlot::replot();
}
